package console

import (
	"bytes"
	"code.google.com/p/joseki/flow"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type Session struct {
	out     io.Writer
	reader  *reader
	scanner *scanner
	parser  *parser
	writer  *writer
	tokens  []*token
	root    *scope
	err     chan error
	stop    <-chan bool
}

func OpenSession(in io.Reader, out io.Writer) *Session {
	session := &Session{
		reader:  &reader{in, make(chan []rune, 1)},
		out:     out,
		scanner: newScanner(),
		root:    &scope{content: make(map[string]interface{})},
		err:     make(chan error, 8),
		stop:    make(chan bool, 1),
	}
	flow.Chain(session.reader, session.scanner, session.parser)
	return session
}

type reader struct {
	in     io.Reader
	chunks chan<- []rune
}

func (r *reader) run() {
	buf := make([]byte, 2048)
	for {
		if n, err := r.in.Read(buf); err == nil {
			fmt.Printf("reading %q\n", string(buf[0:n-1]))
			r.chunks <- bytes.Runes(buf[0 : n-1])
			fmt.Printf("len of r.chunks : %d\n", len(r.chunks))
		} else {
			fmt.Printf("read error : %s", err.Error())
		}
	}
}

func (r *reader) Output() interface{} {
	return r.chunks
}

type writer struct {
	out io.Writer
}

func (s *Session) Run() {
	go s.read()
	go s.eval()
	go s.print()
	s.loop()
}

func (s *Session) loop() {
	for {
		select {
		case <-s.stop:
			return
		}
	}
}

func (s *Session) read() {
	go s.scanner.run()

}

func (s *Session) eval() {
	for {
		select {
		case tok := <-s.scanner.out:
			parse(s, tok)
		}
	}
}

type value interface{}

func parse(s *Session, tok *token) {
	s.tokens = append(s.tokens, tok)
	if tok.t == end {
		if ast, err := s.parser.buildAst(); err == nil {
			evaluate(s, ast)
		} else {
			fmt.Fprintln(s.out, err.Error())
		}
		s.tokens = s.tokens[0:0]
	}
}

func evaluate(s *Session, ast node) {

}
func (s *Session) resolve(name string) value {
	return nil
}

func parseNum(value string) (v value) {
	if strings.ContainsAny(value, "e.") {
		v, _ = strconv.ParseFloat(value, 64)
	} else {
		v, _ = strconv.ParseInt(value, 10, 64)
	}
	return
}

func (s *Session) print() {
	for {
		select {
		case err := <-s.err:
			report(err, s.out)
		}
	}
}

func report(err error, out io.Writer) {
	fmt.Fprintln(out, err.Error())
}
